package se.topshelf.golftracker.core.domain;

import org.hibernate.annotations.Sort;
import org.hibernate.annotations.SortType;

import javax.persistence.*;
import javax.validation.constraints.NotNull;
import java.text.MessageFormat;
import java.util.Date;
import java.util.SortedSet;
import java.util.TreeSet;

/**
 *
 */
@Entity
@Table(name = "TeeTime")
public class TeeTime extends BaseEntity implements Comparable<TeeTime> {
    @NotNull
    @OneToMany(mappedBy = "teeTime",cascade = CascadeType.ALL, orphanRemoval = true)
    @Sort(type = SortType.NATURAL)
    private SortedSet<Round> rounds = new TreeSet<Round>();

    @NotNull
    @ManyToOne
    private Course course;

    @NotNull
    @Temporal(value = TemporalType.TIMESTAMP)
    private Date teeTime;

    public static final int DEFAULT_MAX_PARTICIPANTS = 4;

    private Integer maxParticipants = DEFAULT_MAX_PARTICIPANTS;

    public TeeTime() {
    }

    public TeeTime(Course course, Date teeTimeDate) {
        this.course = course;
        this.teeTime = teeTimeDate;
    }

    public boolean isFullyBooked() {
        return rounds.size() >= maxParticipants;
    }

    public int getCurrentNumberOfParticipants() {
        return rounds.size();
    }

    public int getSlotsLeft() {
        return maxParticipants - getCurrentNumberOfParticipants();
    }

    public SortedSet<Round> getRounds() {
        return rounds;
    }

    public void addRound(Round roundToAdd) {
        if (isFullyBooked()) {
            throw new IllegalStateException(MessageFormat.format("Illegal to add roundToAdd to {0}, max  {1} allowed", this, maxParticipants));
        }
        roundToAdd.setTeeTime(this);
        if (rounds.contains(roundToAdd)) {
            roundToAdd.setTeeTime(null);
            throw new IllegalArgumentException(MessageFormat.format(ILLEGAL_TO_ADD_DUPLICATE_ERROR_MESSAGE, this, roundToAdd));
        }
        rounds.add(roundToAdd);
    }

    public void removeRound(Round roundToRemove) {
        if (rounds.contains(roundToRemove)) {
            rounds.remove(roundToRemove);
        } else {
            throw new IllegalArgumentException(MessageFormat.format(ILLEGAL_TO_REMOVE_ERROR_MESSAGE, this, roundToRemove));
        }
    }

    public void setRounds(SortedSet<Round> rounds) {
        this.rounds = rounds;
    }

    public Course getCourse() {
        return course;
    }

    public void setCourse(Course course) {
        this.course = course;
    }

    public Date getTeeTime() {
        return teeTime;
    }

    public void setTeeTime(Date teeTime) {
        this.teeTime = teeTime;
    }

    public Integer getMaxParticipants() {
        return maxParticipants;
    }

    public void setMaxParticipants(Integer maxParticipants) {
        this.maxParticipants = maxParticipants;
    }

    public int compareTo(TeeTime o) {
        int courseCompare = course.compareTo(o.getCourse());
        if (courseCompare == 0) {
            return teeTime.compareTo(o.getTeeTime());
        }
        return courseCompare;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;

        TeeTime teeTime1 = (TeeTime) o;

        if (!course.equals(teeTime1.course)) return false;
        if (!teeTime.equals(teeTime1.teeTime)) return false;

        return true;
    }

    @Override
    public int hashCode() {
        int result = course.hashCode();
        result = 31 * result + teeTime.hashCode();
        return result;
    }

    @Override
    public String toString() {
        final StringBuilder sb = new StringBuilder();
        sb.append("TeeTime");
        sb.append("{course=").append(course);
        sb.append(", teeTimeDate=").append(teeTime);
        sb.append('}');
        return sb.toString();
    }
}